import type { PixelPoint } from "../commonTypes"
import { Camera } from "../utils/cameraUtil"
import { Sphere } from "../utils/geometryUtil"
import { HitList, type Hitable } from "../utils/hitUtil"
import type { Ray } from "../utils/rayUtil"
import { add, Vec3 } from "../utils/vectorUtil"

/** 定义相机的位置和屏幕的位置 */
const camera = new Camera(
  // 相机位置
  new Vec3(0, 0, 1), //origin
  // 屏幕位置
  new Vec3(-2, -1, -1), //leftBottom
  // 屏幕方向向量
  new Vec3(4, 0, 0), //horizontal
  new Vec3(0, 2, 0), //vertical
)

/** 设置一个球体 */
const sphere1 = new Sphere(new Vec3(0, 0, -1), 0.5)

/** 设置多个球体和世界 */
const sphere2 = new Sphere(new Vec3(1, 0, -1), 0.5)
const sphere3 = new Sphere(new Vec3(-1, 0, -1), 0.5)
const earth = new Sphere(new Vec3(0, -100.5, -1), 100)
const world = new HitList(
  [sphere1, sphere2, sphere3, earth]
)

/**
 * 渲染每个像素
 * @param px 
 * @param width 
 * @param height 
 */
export function renderPixelSphereOne(
  px: PixelPoint,
  width: number,
  height: number) {
  // 解析颜色和透明度，并进行解构赋值
  [px.r, px.g, px.b, px.a] = [...smoothColor(px, width, height, sphere1), 1]
    .map(px => {
      // 转换0-1的颜色到0-255范围内
      return Math.floor(px * 255.9)
    })
}

/**
 * 渲染每个像素
 * @param px 
 * @param width 
 * @param height 
 */
export function renderPixelSphereMore(
  px: PixelPoint,
  width: number,
  height: number) {
  // 解析颜色和透明度，并进行解构赋值
  [px.r, px.g, px.b, px.a] = [...smoothColor(px, width, height, world), 1]
    .map(px => {
      // 转换0-1的颜色到0-255范围内
      return Math.floor(px * 255.9)
    })
}


/**
 * 着色函数
 * @param _x 水平位置比例
 * @param _y 垂直位置比例
 * @returns 
 */
function color(_x: number, _y: number, hitable: Hitable) {
  // 将y轴转换为从低到高的数据
  const [x, y] = [_x, 1 - _y]

  // 获取相机到屏幕的光线
  const ray = camera.getRay(x, y)

  // 获得光线与球的交互信息，时间范围 0 到 无穷
  const hitResult = hitable.hit(ray, 0, Infinity)
  let resColor: Vec3
  if (hitResult) {
    // 存在交互点，设置颜色为绿色
    // resColor = new Vec3(0, 1, 0)
    // 根据法线显示颜色，偏移到 0 1 的范围之内
    resColor = hitResult.hit.normal.add(1).mul(0.5)
  } else {
    // 设置背景色
    resColor = backgroundColor(ray)
  }
  return [resColor.e0, resColor.e1, resColor.e2]
}

function smoothColor(px: PixelPoint, width: number, height: number, hitable: Hitable) {
  // 圆心取点一个
  const cColor = color(px.x / width, px.y / height, hitable)
  // 圆周半径取点8个
  const radius = 0.7
  const count = 9
  for (let i = 0; i < Math.PI * 2; i += Math.PI * 2 / count) {
    const pColor = color(
      (px.x + Math.cos(i) * radius) / width,
      (px.y + Math.sin(i) * radius) / height,
      hitable)
    cColor[0] += pColor[0]
    cColor[1] += pColor[1]
    cColor[2] += pColor[2]
  }
  cColor[0] /= count + 1
  cColor[1] /= count + 1
  cColor[2] /= count + 1
  return cColor

}

/**
 * 获得渐变背景颜色
 * @param ray 
 * @returns 
 */
function backgroundColor(ray: Ray) {
  // 获得光线的单位方向向量
  const unitDirection = ray.direction.unitVec()
  // 获得光线方向 e1垂直 方向上的百分比，并减半缩减到0-1的范围之内
  const colorPercent = (unitDirection.e1 + 1.0) * 0.5
  // 白颜色强度
  const whiteColor = new Vec3(1, 1, 1).mul(1 - colorPercent)
  // 蓝颜色的强度
  const blueColor = new Vec3(0.3, 0.5, 1).mul(colorPercent)
  // 混合蓝色和白色设置为背景色，得到白色到蓝色的渐变
  const resColor = add(whiteColor, blueColor)
  return resColor
}

